package spynx.desl.runtime.impl;

import java.util.ArrayList;
import java.util.List;

import spynx.desl.Argument;
import spynx.desl.Element;
import spynx.desl.Proxy;
import spynx.desl.runtime.IConfigurableProxy;
import spynx.desl.runtime.IDataStorage;
import spynx.desl.runtime.IExecutionContext;
import spynx.desl.runtime.ITextLocator;
import spynx.util.ArgumentSet;

public abstract class AbstractTextLocator implements ITextLocator, IConfigurableProxy {

	protected class ObjectField implements IDataStorage {
		private String fieldname;
		private int fieldIdx;
		
		public ObjectField(String fieldname) {
			this.fieldname = fieldname;
		}
		
		public ObjectField(int fieldidx) {
			this.fieldIdx = fieldidx;
		}

		@Override
		public Object getValue() {
			return fieldname != null ? get(fieldname) :
					get(fieldIdx);
		}

		@Override
		public void setValue(Object value) {
			if (fieldname != null)
				set(fieldname, value);
			else
				set(fieldIdx, value);
		}

		@Override
		public boolean isReadOnly() {
			return false;
		}

		@Override
		public void setReadOnly(boolean readonly) {
		}
	}
	
	private Proxy proxy;
	private String name;
	protected ArgumentSet args;
	private ArgumentSet fields;
	protected IExecutionContext context;

	public abstract void set(String fieldname, Object value);

	public abstract Object get(String fieldname);

	public abstract void set(int fieldIdx, Object value);

	public abstract Object get(int fieldIdx);

	public abstract int locate(IExecutionContext context, int sidx, CharSequence input);

	public AbstractTextLocator() {
		super();
	}
	
	public Object getValue() {
		//return args.getArgument(context, 0, "");
		return this;
	}
	
	public String getPatternText() {
		return args.getArgument(context, 0, "");
	}

	protected ArgumentSet getFields() {
		if (fields == null) {
			Argument fieldsArg = args.getArgumentObject("fields");
			if (fieldsArg != null) 
				fields = new ArgumentSet(fieldsArg.getChildren(Argument.class));
			else
				fields = ArgumentSet.EMPTY;
		}
		return fields;
	}

	@Override
	public void setValue(Object value) {
		
	}

	@Override
	public String getClassName() {
		return null;
	}

	@Override
	public Iterable<String> getFieldNames() {
		List<Argument> names = getFields().getArguments();
		List<String> list = new ArrayList<String>();
		if (names != null)
			for(Argument arg: names)
				list.add(arg.getName());
		return list;
	}

	@Override
	public String getName() {
		return this.name;
	}

	@Override
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public boolean isPersist() {
		return false;
	}

	@Override
	public boolean isReadOnly() {
		return true;
	}

	@Override
	public void setReadOnly(boolean readonly) {
		
	}

	@Override
	public Object applyOperator(IExecutionContext context, String operator, Object... args) {
		Object param = context.resolveValue(args[0]);
		switch (operator.charAt(0)) {
		case '.': return new ObjectField((String)param);
		case '[': return new ObjectField((Integer)param);
		}
		throw new RuntimeException("Invalid operator: " + operator);
	}

	@Override
	public void setProxyInfo(Proxy proxy) {
		this.proxy = proxy;
		if (this.args != null)
			addProxyArgument(proxy);
	}

	@Override
	public void setArguments(List<Argument> arguments) {
		this.args = new ArgumentSet(arguments);
		if (proxy != null)
			addProxyArgument(proxy);
		
	}

	private void addProxyArgument(Proxy proxy) {
		if (proxy.getChildren().size() > 0) {
			Element arg = proxy.getChildren().get(0);
			args.getArguments().addAll(arg.getChildren(Argument.class));
		}
	}

	public void setContext(IExecutionContext context) {
		this.context = context;
	}

	public IExecutionContext getContext() {
		return context;
	}

}